// synch.cc 
//	Routines for synchronizing threads.  Three kinds of
//	synchronization routines are defined here: semaphores, locks 
//   	and condition variables (the implementation of the last two
//	are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"

#ifdef NETWORK
#include "post.h"
#endif

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	Initialize a semaphore, so that it can be used for synchronization.
//
//	"debugName" is an arbitrary name, useful for debugging.
//	"initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore(char* debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	De-allocate semaphore, when no longer needed.  Assume no one
//	is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
// 	Wait until semaphore value > 0, then decrement.  Checking the
//	value and decrementing must be done atomically, so we
//	need to disable interrupts before checking the value.
//
//	Note that Thread::Sleep assumes that interrupts are disabled
//	when it is called.
//----------------------------------------------------------------------

void
Semaphore::P()
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
    
    while (value == 0) { 			// semaphore not available
		queue->Append((void *)currentThread);	// so go to sleep
		currentThread->Sleep();
    } 
    value--; 					// semaphore available, 
	// consume its value
    
    (void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
// 	Increment semaphore value, waking up a waiter if necessary.
//	As with P(), this operation must be atomic, so we need to disable
//	interrupts.  Scheduler::ReadyToRun() assumes that threads
//	are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);
	
    thread = (Thread *)queue->Remove();
    if (thread != NULL)	   // make thread ready, consuming the V immediately
		scheduler->ReadyToRun(thread);
    value++;
    (void) interrupt->SetLevel(oldLevel);
}

// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!
Lock::Lock(char* debugName) {
	name = debugName;
	status = FREE;	// First initialize the lock status to free;
	waitingQueue = new List;
}

Lock::~Lock() {
	delete waitingQueue;	// Delete the queue
}

void Lock::Acquire() {
	IntStatus oldStatus = interrupt->SetLevel(IntOff);	// Disable interrupts
	if (owner != currentThread) {	// The current thread does not hold the lock
		if (status == FREE) {	// The lock is available
			status = BUSY;	// Set lock status to busy
			owner = currentThread;	// Make the current thread to be the owner of the lock
		}else {	// The lock is unavailable
			waitingQueue->Append((void *)currentThread);	// Add the thread to the waiting queue
			currentThread->Sleep();	// Waiting for the lock
		}
	}
	// If the current thread already holds the lock, we do nothing
	(void) interrupt->SetLevel(oldStatus);	// Restore interrupts
}

void Lock::Release() {
	Thread *thread;
	IntStatus oldStatus = interrupt->SetLevel(IntOff);	// Disable interrupts
	if (owner == currentThread) {	// The current thread holds the lock
		if (!waitingQueue->IsEmpty()) {	// There are some threads waiting for the lock
			thread = (Thread *)waitingQueue->Remove();	// Remove the first thread in the queue
			if (thread != NULL) {
				scheduler->ReadyToRun(thread);	// Put it in the ready queue
				owner = thread;	// Set it the owner of the lock
			}
		}else {	// There is no thread waiting for the lock
			status = FREE;	// Set the lock free
			owner = NULL;	// Clear the owner field
		}
	}
	// If the current thread doesn't actual own the lock, it cannot release the lock
	// We do nothing because this error should be evicted by programmer.
	(void) interrupt->SetLevel(oldStatus);	//re-enable interrupts
}

bool Lock::isHeldByCurrentThread() {
	// Check if the current thread owns the lock
	return owner == currentThread;
}

Condition::Condition(char* debugName) {
	name = debugName;
	cvWaitingQueue = new List;
	waitingLock = NULL;
}

Condition::~Condition() {
	delete waitingLock;
	waitingLock = NULL;
	delete cvWaitingQueue;
}

void Condition::Wait(Lock* conditionLock) {
	IntStatus oldStatus = interrupt->SetLevel(IntOff);	// Disable interrupts
	if (conditionLock == NULL) {
		printf("Error: the lock is empty\n");
	}	// Check the parameter
	if (waitingLock == NULL) {	// This is the first thread calling wait	
		waitingLock = conditionLock;	// Set the waitingLock to the parameter
		//printf("First wait %s\n", name);
	}
	if (waitingLock == conditionLock) {	// One condition variable can only wait on one lock
		conditionLock->Release();	// Exit the monitor
		cvWaitingQueue->Append((void *)currentThread);	// Add the current thread to the waiting queue
		currentThread->Sleep();	// Current thread will waiting
		conditionLock->Acquire();	// Reacquire the lock after being wake up
	}else {
		// The condition tries to wait on a different lock.
		printf("Error: you cannot wait on a different lock\n");
		//cout << waitingLock << "   " << conditionLock<<endl;
	}
	(void) interrupt->SetLevel(oldStatus);	// Restore interrupts
}

void Condition::Signal(Lock* conditionLock) {
	Thread *thread;
	IntStatus oldStatus = interrupt->SetLevel(IntOff);	// Disable interrupts
	if (!cvWaitingQueue->IsEmpty() && waitingLock == conditionLock) {	
		// There are some threads waiting
		thread = (Thread *)cvWaitingQueue->Remove();	// Remove one thread in the waiting queue
		scheduler->ReadyToRun(thread);	// Put it in the ready queue
		if (cvWaitingQueue->IsEmpty()) {	// there is no more waiting threads
			waitingLock = NULL;	// Clear the lock field
		}
	}else if (waitingLock != conditionLock) {
		// The condition can only signal those threads waiting on the same lock
		printf("Error: The condition can only signal those threads waiting on the same lock\n");
	}
	(void) interrupt->SetLevel(oldStatus);	// Restore interrupts
}

void Condition::Broadcast(Lock* conditionLock) {
	// Signal every one in the waiting queue
	while (!cvWaitingQueue->IsEmpty()) {
		Signal(conditionLock);
	}
}

#ifdef NETWORK
ServerLock::ServerLock(char* debugName) {
	ownerID = -1;			// the machine ID that currently owns the lock
	ownerBox= -1;
	name = debugName;
	status = FREE;	// First initialize the lock status to free;
	waitingQueue = new List;
}

ServerLock::~ServerLock() {
	delete[] name;
	name = NULL;
	delete waitingQueue;	// Delete the queue
}

int ServerLock::Acquire() {
	IntStatus oldStatus = interrupt->SetLevel(IntOff);	// Disable interrupts
	if (ownerID != currentID || ownerBox != currentBox) {	// The current thread does not hold the lock
		if (status == FREE) {	// The lock is available
			cout<<"Lock availeble"<<endl;
			status = BUSY;	// Set lock status to busy
			ownerID = currentID;
			ownerBox = currentBox;	// Make the current thread to be the owner of the lock
			bool success = postOffice->Send(Replymsg->outPktHdr, Replymsg->outMailHdr, Replymsg->data); 
    		if ( !success ) {
      		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      		interrupt->Halt();
    		}
		}else {	// The lock is unavailable
			waitingQueue->Append((void *)Replymsg);	// Add the thread to the waiting queue
		}
		return 1;
	}
		
	// If the current thread already holds the lock, we do nothing
	(void) interrupt->SetLevel(oldStatus);	// Restore interrupts
	return 2;
}

int ServerLock::Release() {
	ACKInfor *msg;
	IntStatus oldStatus = interrupt->SetLevel(IntOff);	// Disable interrupts
	if (ownerID == currentID && ownerBox == currentBox) {	// The current thread holds the lock
		if (!waitingQueue->IsEmpty()) {	// There are some threads waiting for the lock
			msg = (ACKInfor *)waitingQueue->Remove();	// Remove the first thread in the queue
			if (msg != NULL) {
				ownerID = msg->outPktHdr.to;
			  ownerBox = msg->outMailHdr.to;
        bool success = postOffice->Send(msg->outPktHdr, msg->outMailHdr, msg->data);
    		if ( !success ) {
      		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      		interrupt->Halt();
    		}
			}
		}else {	// There is no thread waiting for the lock
			status = FREE;	// Set the lock free
			ownerID = -1;
			ownerBox = -1;
		}
		return 1;
	}
		
	// If the current thread doesn't actual own the lock, it cannot release the lock
	// We do nothing because this error should be evicted by programmer.
	cout<<"ccacacaac"<<endl;
	(void) interrupt->SetLevel(oldStatus);	//re-enable interrupts
	return 2;
}

bool ServerLock::isHeldByCurrentMM() {
	// Check if the current thread owns the lock
	return (ownerID == currentID && ownerBox == currentBox );
}


ServerCondition::ServerCondition(char* debugName) {
	delete[] name;
	name = debugName;
	cvWaitingQueue = new List;
	waitingLock = -1;
}

ServerCondition::~ServerCondition() {
	waitingLock = -1;
	delete cvWaitingQueue;
	name = NULL;
}

int ServerCondition::Wait(int conditionLock) {
	IntStatus oldStatus = interrupt->SetLevel(IntOff);	// Disable interrupts
	if (conditionLock < 0) {
		printf("Error: the lock is empty\n");
		return 1;
	}	// Check the parameter
	if (waitingLock == -1) {	// This is the first thread calling wait	
		waitingLock = conditionLock;	// Set the waitingLock to the parameter
		//printf("First wait %s\n", name);
		
	}
	if (waitingLock == conditionLock) {	// One condition variable can only wait on one lock
		kernelServerLocks[conditionLock].Serverlock->Release();	// Exit the monitor
		cvWaitingQueue->Append((void *)Replymsg);	// Add the current thread to the waiting queue		
	  return 1;
	}else {
		// The condition tries to wait on a different lock.
		printf("Error: you cannot wait on a different lock\n");
		//cout << waitingLock << "   " << conditionLock<<endl;
	}
	(void) interrupt->SetLevel(oldStatus);	// Restore interrupts
	return 2;
}

int ServerCondition::Signal(int conditionLock) {
	ACKInfor *msg;
	IntStatus oldStatus = interrupt->SetLevel(IntOff);	// Disable interrupts
	if (kernelServerLocks[conditionLock].Serverlock->ownerID != currentID || kernelServerLocks[conditionLock].Serverlock->ownerBox != currentBox){
		printf("Error: The condition can only signal others if you have the lock\n");
		return 1;
	}
	else if (!cvWaitingQueue->IsEmpty() && waitingLock == conditionLock) {	
		// There are some threads waiting
		msg = (ACKInfor *)cvWaitingQueue->Remove();	// Remove one thread in the waiting queue
    kernelServerLocks[conditionLock].Serverlock->waitingQueue->Append((void *)msg);
    /*
    bool success = postOffice->Send(msg->outPktHdr, msg->outMailHdr, msg->data); 
    		if ( !success ) {
      		printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      		interrupt->Halt();
    		}
    		*/
		if (cvWaitingQueue->IsEmpty()) {	// there is no more waiting threads
			waitingLock = -1;	// Clear the lock field
		}
		return 2;
	}else if (waitingLock != conditionLock) {
		// The condition can only signal those threads waiting on the same lock
		printf("Error: The condition can only signal those threads waiting on the same lock\n");
	}
	(void) interrupt->SetLevel(oldStatus);	// Restore interrupts
	return 3;
}

void ServerCondition::Broadcast(int conditionLock) {
	// Signal every one in the waiting queue
	while (!cvWaitingQueue->IsEmpty()) {
		Signal(conditionLock);
	}
}
#endif

